package model.tokens;

import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

import model.semantics.Node;

/**
 * Automaton class. Represents the current state of the automaton parsing the
 * language. Holds everything that the Tokens need to process.
 * 
 * @author Sterling Dorminey
 * 
 */
public class Automaton<T> {
    /* These protected fields are visible to the tokens, which manipulate them. */
    protected Queue<Token<T>> tokens;
    protected Token<T> lastToken;
    protected Stack<Token<T>> operatorStack;
    protected Deque<Node<T>> output;

    private String myTokenFile;
    private String mySyntaxFile;

    /**
     * Construct an automaton from a queue of tokens (e.g. from a Lexer.)
     */
    public Automaton(Queue<Token<T>> _tokens, String tokenFile,
            String syntaxFile) {
        lastToken = null;
        tokens = _tokens;
        myTokenFile = tokenFile;
        mySyntaxFile = syntaxFile;
        operatorStack = new Stack<Token<T>>();
        output = new LinkedList<Node<T>>();
    }

    /**
     * Returns a copy of this automaton with blank state.
     */
    public Automaton<T> makeBlankAutomaton() {
        return new Automaton<T>(tokens, myTokenFile, mySyntaxFile);
    }

    /**
     * Merge the state of another automaton into this. Useful for recursive
     * calls.
     * 
     * @param other
     *            the other automaton.
     */
    public void merge(Automaton<T> other) {
        operatorStack.addAll(other.operatorStack);
        output.addAll(other.output);
    }

    /**
     * Process all tokens.
     */
    public void processTokens() {
        while (!tokens.isEmpty()) {
            Token<T> token = tokens.poll();
            token.process(this);
            lastToken = token;
        }

        Token.shuntFunctionsToOutput(operatorStack, output);
    }

    public void processTokens(int numTokens) {
        for (int i = 0; i < numTokens; i++) {
            Token<T> token = tokens.poll();
            token.process(this);
            lastToken = token;
        }
        Token.shuntFunctionsToOutput(operatorStack, output);
    }

    /**
     * Empties the operator stack, instantiating everything.
     */
    public void fillOutputQueue() {
        while (!operatorStack.isEmpty()) {
            NodeToken<T> token = (NodeToken<T>) operatorStack.pop();
            output.add(token.makeNewInstance(output));
        }
    }

    public String getTokenFile() {
        return myTokenFile;
    }

    public String getSyntaxFile() {
        return mySyntaxFile;
    }

    public Deque<Node<T>> getOutput() {
        return output;
    }
}
